package com.yuxl.admin.manager.auth.security.controller;

import com.yuxl.admin.common.core.utils.R;
import com.yuxl.admin.common.security.config.JwtProperties;
import com.yuxl.admin.common.security.domain.AuthUserDetails;
import com.yuxl.admin.common.security.domain.LoginReq;
import com.yuxl.admin.common.security.service.JwtService;
import com.yuxl.admin.common.security.service.RedisTokenService;
import com.yuxl.admin.common.socket.service.WebSocketService;
import com.yuxl.admin.manager.auth.core.entity.AuthRole;
import com.yuxl.admin.manager.auth.core.entity.AuthUser;
import io.jsonwebtoken.Claims;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Mono;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/auth")
@Tag(name = "认证管理", description = "用户登录和注销操作")
public class AuthController {


    private final JwtService jwtService;
    private final JwtProperties jwtProperties;
    private final RedisTokenService redisTokenService;

    private final WebSocketService webSocketService;

    /**
     * 用户登录
     *
     * @param loginReq 登录请求对象，包含用户名和密码
     * @return 包含JWT令牌的Map
     */
    @Operation(summary = "用户登录", description = "验证用户凭据并返回JWT令牌")
    @PostMapping("/login")
    public Mono<R<Map<String, String>>> login(@RequestBody LoginReq loginReq) {
        String username = loginReq.getUsername();
        String password = loginReq.getPassword();
        log.debug("用户 {} 尝试登录", username);
        return jwtService.authenticateUser(username, password)
                .flatMap(authenticated -> {
                    if (authenticated) {
                        return jwtService.generateNewToken(username)
                                .flatMap(tokenMap -> {
                                    String token = tokenMap.get(jwtProperties.getHeaderKey()).substring(jwtProperties.getBearerToken().length()); // Remove "Bearer "
                                    List<AuthRole> roles = jwtService.extractRoles(token);
                                    List<SimpleGrantedAuthority> authorities = roles.stream()
                                            .map(k->new SimpleGrantedAuthority(k.getMark()))
                                            .collect(Collectors.toList());
                                    UsernamePasswordAuthenticationToken auth =
                                            new UsernamePasswordAuthenticationToken(username, token, authorities);

                                    return R.ok(Mono.just(tokenMap)
                                            .contextWrite(ReactiveSecurityContextHolder.withAuthentication(auth)));
                                });
                    } else {
                        return R.restResult(Mono.empty(), HttpStatus.UNAUTHORIZED, "用户名或密码错误");
                    }
                });
    }


    /**
     * 用户注销
     *
     * @return 包含注销结果的Map
     */
    @Operation(summary = "用户注销", description = "注销用户并清除相关会话")
    @PostMapping("/logout")
    public Mono<R<Map<String, String>>> logout() {
        return R.ok(ReactiveSecurityContextHolder.getContext()
                .flatMap(securityContext -> {
                    Authentication authentication = securityContext.getAuthentication();
                    if (authentication != null && authentication.getName() != null) {
                        String username = authentication.getName();
                        log.debug("用户 {} 正在注销", username);
                        return redisTokenService.deleteToken(username)
                                .then(webSocketService.closeConnection(username))
                                .thenReturn(Map.of("message", "注销成功"))
                                .doOnSuccess(result -> log.debug("用户 {} 注销成功", username));
                    } else {
                        log.warn("尝试注销未登录的用户");
                        return Mono.just(Map.of("message", "注销成功"));
                    }
                })
                .defaultIfEmpty(Map.of("message", "注销成功")));
    }

    /**
     * 获取当前用户信息
     *
     * @return 包含注销结果的Map
     */
    @Operation(summary = "获取当前用户信息", description = "获取当前用户信息")
    @GetMapping("/userInfo")
    public Mono<R<AuthUserDetails>> userInfo() {
        return R.ok(jwtService.getCurrentUserInfo());
    }
}
